package com.lhkj.ct.meta.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.lhkj.ct.base.constants.SystemConstants;
import com.lhkj.ct.base.enums.GlobalStatus;
import com.lhkj.ct.base.enums.ReturnStatus;
import com.lhkj.ct.base.exception.BusinessException;
import com.lhkj.ct.base.model.LoginUser;
import com.lhkj.ct.base.model.Option;
import com.lhkj.ct.base.mybatis.extension.service.impl.IServiceImpl;
import com.lhkj.ct.base.utils.Asserts;
import com.lhkj.ct.base.utils.EnumUtils;
import com.lhkj.ct.base.utils.ShiroUtil;
import com.lhkj.ct.base.utils.TreeUtil;
import com.lhkj.ct.meta.admin.common.enums.MenuType;
import com.lhkj.ct.meta.admin.mapper.SysMenuMapper;
import com.lhkj.ct.meta.admin.mapper.SysRoleMenuMapper;
import com.lhkj.ct.meta.admin.model.entity.TblMenu;
import com.lhkj.ct.meta.admin.model.entity.TblRoleMenu;
import com.lhkj.ct.meta.admin.model.vo.MenuVo;
import com.lhkj.ct.meta.admin.model.vo.RouterVo;
import com.lhkj.ct.meta.admin.service.SysMenuService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author jobob
 * @since 2023-08-22
 */
@Service
public class SysMenuServiceImpl extends IServiceImpl<SysMenuMapper, TblMenu> implements SysMenuService {

    @Resource
    private SysRoleMenuMapper sysRoleMenuMapper;
    /**
     * 保存菜单
     */
    @Override
    public int saveMenu(TblMenu menu) {
        Asserts.assertFalse(StringUtils.equals(menu.getId(), menu.getParentId()), "上级菜单不能是自己");
        if (StringUtils.isNotBlank(menu.getParentId()) && !SystemConstants.ROOT_NODE_ID.equals(menu.getParentId())) {
            TblMenu pMenu = getById(menu.getParentId());
            if (pMenu == null) {
                throw new BusinessException(ReturnStatus.SC_BAD_REQUEST, "上级菜单不存在！");
            }
        }
        MenuType menuType = menu.getType();  // 菜单类型
        switch (menuType) {
            case CATALOG:  // 目录
                if (StringUtils.equals(menu.getParentId(), SystemConstants.ROOT_NODE_ID)) {
                    // tips 一级目录Layout
                    menu.setComponent("Layout");
                }else{
                    // tips 二级目录ParentLayout
                    menu.setComponent("ParentLayout");
                }
                menu.setFullPath(null);
                menu.setComponentName(null);
                menu.setPerm(null);
                break;
            case MENU:
                if (!menu.getFullPath().startsWith("/")){
                    menu.setFullPath("/"+ menu.getFullPath());
                }
                if (!menu.getComponent().startsWith("/")){
                    menu.setComponent("/"+ menu.getComponent());
                }
                break;
            case BUTTON:
                menu.setPath(null);
                menu.setFullPath(null);
                menu.setComponent(null);
                menu.setComponentName(null);
                break;
            case EXTLINK:
                // 外链
                menu.setFullPath(null);
                menu.setComponent(null);
                menu.setComponentName(null);
                menu.setPerm(null);
                break;
        }
        String treePath = generateMenuTreePath(menu.getParentId());
        menu.setTreePath(treePath);
        return StringUtils.isBlank(menu.getId()) ? insert(menu) : this.updateMenu(menu)  ;
    }

    public int updateMenu(TblMenu entity) {
        UpdateWrapper<TblMenu> wrapper = new UpdateWrapper<>();
        wrapper.lambda()
                .set(TblMenu::getParentId, entity.getParentId())
                .set(StringUtils.isNotBlank(entity.getName()), TblMenu::getName, entity.getName())
                .set(TblMenu::getType, entity.getType())
                .set(TblMenu::getIcon, entity.getIcon())
                .set(TblMenu::getPath, entity.getPath())
                .set(TblMenu::getFullPath, entity.getFullPath())
                .set(TblMenu::getComponent, entity.getComponent())
                .set(TblMenu::getComponentName, entity.getComponentName())
                .set(TblMenu::getPerm, entity.getPerm())
                .set(TblMenu::getMeta, entity.getMeta())
                .set(TblMenu::getNoCache, entity.getNoCache())
                .set(TblMenu::getHidden, entity.getHidden())
                .set(Objects.nonNull(entity.getStatus()), TblMenu::getStatus, entity.getStatus())
                .set(Objects.nonNull(entity.getSort()), TblMenu::getSort, entity.getSort())
                .set(TblMenu::getTreePath, entity.getTreePath())
                .eq(TblMenu::getId, entity.getId());
        return getBaseMapper().update(null, wrapper);
    }

    @Override
    public boolean updateMenuVisible(String menuId, Integer visible) {
        IEnum<Integer> status = EnumUtils.getByValue(visible, GlobalStatus.class);
        Asserts.assertTrue(status != null, "状态标识不合法！");
        return  SqlHelper.retBool(this.getBaseMapper().update(null,new LambdaUpdateWrapper<TblMenu>()
                .eq(TblMenu::getId, menuId)
                .set(TblMenu::getStatus, visible))
        );
    }

    @Override
    public int deleteMenu(String id) {
        return this.getBaseMapper().delete(new LambdaQueryWrapper<TblMenu>()
                .eq(TblMenu::getId, id)
                .or()
                .apply("CONCAT (',',tree_path,',') LIKE CONCAT('%,',{0},',%')", id));
    }

    @Override
    public boolean hasChildByMenuId(String menuId) {
        Long count = this.getBaseMapper().selectCount(
                new LambdaQueryWrapper<TblMenu>().select(TblMenu::getId).eq(TblMenu::getParentId, menuId)
        );
        return count > 0;
    }

    @Override
    public boolean checkMenuExistRole(String menuId) {
        Long count = sysRoleMenuMapper.selectCount(
                new LambdaQueryWrapper<TblRoleMenu>().select(TblRoleMenu::getId).eq(TblRoleMenu::getMenuId, menuId)
        );
        return count > 0;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<MenuVo> listMenus(TblMenu queryParams) {
        // 查询所有菜单
        List<MenuVo> menus = this.getBaseMapper().listMenus( new LambdaQueryWrapper<TblMenu>()
                .like(StringUtils.isNotBlank(queryParams.getName()), TblMenu::getName, queryParams.getName())
                .orderByAsc(TblMenu::getSort));
        if (menus.isEmpty()) {
            return new ArrayList<>();
        }
        return TreeUtil.builderTree(menus);
    }

    @Override
    public List<Option<String>> listMenuOptions() {
        List<TblMenu> menuList = this.getBaseMapper().listMenuOptions(
                new LambdaQueryWrapper<TblMenu>().orderByAsc(TblMenu::getSort));
        return recurMenuOptions(SystemConstants.ROOT_NODE_ID, menuList);
    }

    @Override
    public List<RouterVo> getRoutesByLoginUser() {
        List<TblMenu> menuList;
        if (ShiroUtil.isRoot()) {
            menuList = getBaseMapper().selectList(new LambdaQueryWrapper<TblMenu>().eq(TblMenu::getStatus, GlobalStatus.NORMAL).orderByAsc(TblMenu::getSort));
        }else{
            menuList = this.getBaseMapper().listRoutes(ShiroUtil.getAuthUserInfo().getUserId());
        }
        List<TblMenu> menuTree = TreeUtil.builderTree(menuList);
        return recurRoutes(menuTree);
    }

    @Override
    public Set<String> getPermsByUserId(LoginUser user) {
        if (null == user) {
            return new HashSet<>();
        }
        if (user.isRoot()) {
            return this.getBaseMapper().getPermsByUserId("0");
        }
        return this.getBaseMapper().getPermsByUserId(user.getUserId());
    }

    /**
     * 递归生成菜单下拉层级列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return
     */
    private static List<Option<String>> recurMenuOptions(String parentId, List<TblMenu> menuList) {
        List<Option<String>> menus = CollectionUtil.emptyIfNull(menuList).stream()
                .filter(menu -> menu.getParentId().equals(parentId))
                .map(menu -> new Option<>(menu.getId(), menu.getName(), recurMenuOptions(menu.getId(), menuList)))
                .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
        return menus;
    }

    /**
     * 菜单路径生成
     *
     * @param parentId 父ID
     * @return 父节点路径以英文逗号(, )分割，eg: 1,2,3
     */
    private String generateMenuTreePath(String parentId) {
        String treePath = null;
        if (SystemConstants.ROOT_NODE_ID.equals(parentId)) {
            treePath = parentId;
        } else {
            TblMenu parent = this.getById(parentId);
            if (parent != null) {
                treePath = parent.getTreePath() + "," + parent.getId();
            }
        }
        return treePath;
    }

    /**
     * 递归生成菜单路由层级列表
     *
     * @param menuTree 菜单树级列表
     * @return
     */
    private List<RouterVo> recurRoutes(List<TblMenu> menuTree) {
        List<RouterVo> routers = new LinkedList<RouterVo>();
        for (TblMenu menu : menuTree) {
            if (menu.getType() == MenuType.BUTTON){
                continue;
            }
            RouterVo router = new RouterVo();
            router.setName(menu.getName());
            router.setPath(menu.getPath());
            router.setComponent(menu.getComponent());
            router.setComponentName(menu.getComponentName());
            router.setFullPath(menu.getFullPath());
            buildRouteMeta(menu, router);
            if (menu.getType() == MenuType.MENU){
                Set<String> permission = CollectionUtil.emptyIfNull(menu.getChildren()).stream().filter(m -> m.getType() == MenuType.BUTTON).map(TblMenu::getPerm).collect(Collectors.toSet());
                permission.add(menu.getPerm());
                router.getMeta().put("permission", permission);
            }
            router.setChildren(recurRoutes(menu.getChildren().stream().filter(m -> m.getType() != MenuType.BUTTON).collect(Collectors.toList())));
            routers.add(router);
        }
        return routers;
    }

    private void buildRouteMeta(TblMenu menu , RouterVo router) {
        router.builderMeta(menu.getMeta());
        router.getMeta().put("title", menu.getName());
//        router.getMeta().put("hidden", !Convert.toBool(menu.getVisible(), true));
//        router.getMeta().put("noCache", !Convert.toBool(menu.getCache(), true));
//        router.getMeta().put("icon", menu.getIcon());
//        router.getMeta().put("alwaysShow", CollectionUtils.isNotEmpty(menu.getChildren()));
    }

}
